commonlibsse_ng\re\c\Calendar/day.rs
1/// Represents the days of the week.
2#[repr(u32)]
3#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
4pub enum Week {
5 #[default]
6 Sundas,
7 Morndas,
8 Tirdas,
9 Middas,
10 Turdas,
11 Fredas,
12 Loredas,
13 // Total, // unused
14}
15
16impl Week {
17 /// Converts the `Week` enum into a string representation.
18 ///
19 /// # Example
20 /// ```
21 /// # use commonlibsse_ng::re::Calendar::Week;
22 /// let day = Week::Sundas;
23 /// assert_eq!(day.as_str(), "Sundas");
24 /// ```
25 #[inline]
26 pub const fn as_str(&self) -> &'static str {
27 match *self {
28 Self::Sundas => "Sundas",
29 Self::Morndas => "Morndas",
30 Self::Tirdas => "Tirdas",
31 Self::Middas => "Middas",
32 Self::Turdas => "Turdas",
33 Self::Fredas => "Fredas",
34 Self::Loredas => "Loredas",
35 }
36 }
37
38 /// Converts a `u32` value into a corresponding `Week` variant.
39 ///
40 /// Returns `None` if the value is outside the range of valid days (0-6).
41 ///
42 /// # Example
43 /// ```
44 /// # use commonlibsse_ng::re::Calendar::Week;
45 /// let day = Week::from_u32(2);
46 /// assert_eq!(day, Some(Week::Tirdas));
47 /// ```
48 ///
49 #[inline]
50 pub const fn from_u32(v: u32) -> Option<Self> {
51 Some(match v {
52 0 => Self::Sundas,
53 1 => Self::Morndas,
54 2 => Self::Tirdas,
55 3 => Self::Middas,
56 4 => Self::Turdas,
57 5 => Self::Fredas,
58 6 => Self::Loredas,
59 _ => return None,
60 })
61 }
62}
63
64/// Represents a day in a 0-based game.
65///
66/// This usually takes the range `0.0..=30.0` range.
67#[derive(Debug, Default, Clone, Copy, PartialEq)]
68#[repr(transparent)]
69pub struct GameDay(pub f32);
70
71impl GameDay {
72 /// The default `GameDay` value (0.0) at compile time.
73 pub const DEFAULT: Self = Self(0.0);
74
75 /// Creates a new `GameDay` instance with the specified value.
76 ///
77 /// # Example
78 /// ```
79 /// # use commonlibsse_ng::re::Calendar::GameDay;
80 /// let game_day = GameDay::new(5.0);
81 /// assert_eq!(game_day.0, 5.0);
82 /// ```
83 #[inline]
84 pub const fn new(value: f32) -> Self {
85 Self(value)
86 }
87
88 /// Returns the day of the week (0-6), 0 based value.
89 ///
90 /// # Example
91 /// ```
92 /// # use commonlibsse_ng::re::Calendar::GameDay;
93 /// let game_day = GameDay::new(3.0);
94 /// assert_eq!(game_day.day_of_week(), 3);
95 /// ```
96 #[inline]
97 pub const fn day_of_week(&self) -> u32 {
98 (self.0 as u32) % 7
99 }
100
101 /// Clamps the day value based on the month's maximum days.
102 ///
103 /// When month is in the range of 1 to 12, a valid value is returned.
104 ///
105 /// # Example
106 /// ```
107 /// use commonlibsse_ng::re::Calendar::GameDay;
108 /// let game_day = GameDay::new(32.0);
109 /// assert_eq!(game_day.to_clamp_day(2), 28); // Sun's Dawn (28 days)
110 ///
111 /// assert_eq!(game_day.to_clamp_day(0), 31); // Underflow (Fallback to 31 days)
112 /// assert_eq!(game_day.to_clamp_day(12), 31); // Overflow (Fallback to 31 days)
113 /// assert_eq!(game_day.to_clamp_day(300), 31); // Overflow (Fallback to 31 days)
114 /// ```
115 #[inline]
116 pub const fn to_clamp_day(self, month: u32) -> u32 {
117 /// Days in each month
118 pub const DAYS_IN_MONTH: [u8; 12] = [
119 31, // Morning Star
120 28, // Sun's Dawn
121 31, // First Seed
122 30, // Rain's Hand
123 31, // Second Seed
124 30, // Midyear
125 31, // Sun's Height
126 31, // Last Seed
127 30, // Hearthfire
128 31, // Frostfall
129 30, // Sun's Dusk
130 31, // Evening Star
131 ];
132 let max_days = match month {
133 1..=12 => DAYS_IN_MONTH[(month - 1) as usize] as u32,
134 _ => 31,
135 };
136 let n = self.0 as u32;
137 if n < max_days { n } else { max_days }
138 }
139
140 /// Returns the ordinal suffix for the day (e.g., `st`, `nd`, `rd`, `th`).
141 ///
142 /// # Example
143 /// ```
144 /// use commonlibsse_ng::re::Calendar::GameDay;
145 /// let game_day = GameDay::new(21.0);
146 /// assert_eq!(game_day.ordinal_suffix(), "st");
147 /// ```
148 #[inline]
149 pub const fn ordinal_suffix(&self) -> &'static str {
150 match self.0 as i32 {
151 1 | 21 | 31 => "st",
152 2 | 22 => "nd",
153 3 | 23 => "rd",
154 _ => "th",
155 }
156 }
157
158 /// Converts `GameDay` into a `Week` enum.
159 ///
160 /// Returns `None` if the `GameDay` value is out of range (greater than 7.0).
161 ///
162 /// # Example
163 /// ```
164 /// use commonlibsse_ng::re::Calendar::{GameDay, Week};
165 /// let game_day = GameDay::new(1.0);
166 /// assert_eq!(game_day.to_week(), Some(Week::Morndas));
167 /// ```
168 #[inline]
169 pub const fn to_week(self) -> Option<Week> {
170 Some(match self.0 as u32 {
171 0 => Week::Sundas,
172 1 => Week::Morndas,
173 2 => Week::Tirdas,
174 3 => Week::Middas,
175 4 => Week::Turdas,
176 5 => Week::Fredas,
177 6 => Week::Loredas,
178 _ => return None,
179 })
180 }
181}
182
183#[cfg(test)]
184mod tests {
185 use super::*;
186
187 #[test]
188 fn test_week_from_u32() {
189 assert_eq!(Week::from_u32(0), Some(Week::Sundas));
190 assert_eq!(Week::from_u32(1), Some(Week::Morndas));
191 assert_eq!(Week::from_u32(6), Some(Week::Loredas));
192 assert_eq!(Week::from_u32(7), None);
193 assert_eq!(Week::from_u32(100), None);
194 }
195
196 #[test]
197 fn test_game_day_to_week() {
198 assert_eq!(GameDay::new(0.0).to_week(), Some(Week::Sundas));
199 assert_eq!(GameDay::new(3.0).to_week(), Some(Week::Middas));
200 assert_eq!(GameDay::new(6.0).to_week(), Some(Week::Loredas));
201 assert_eq!(GameDay::new(7.0).to_week(), None); // Out of valid range
202 }
203
204 #[test]
205 fn test_game_day_clamp() {
206 assert_eq!(GameDay::new(32.0).to_clamp_day(2), 28); // February (28 days)
207 assert_eq!(GameDay::new(32.0).to_clamp_day(4), 30); // April (30 days)
208 assert_eq!(GameDay::new(32.0).to_clamp_day(12), 31); // December (31 days)
209 }
210
211 #[test]
212 fn test_ordinal_suffix() {
213 assert_eq!(GameDay::new(1.0).ordinal_suffix(), "st");
214 assert_eq!(GameDay::new(2.0).ordinal_suffix(), "nd");
215 assert_eq!(GameDay::new(3.0).ordinal_suffix(), "rd");
216 assert_eq!(GameDay::new(4.0).ordinal_suffix(), "th");
217 }
218}